#pragma once

#include <iostream>
#include <cassert>
#include <functional>
#include <unordered_map>

#include "log.hpp"
#include "sock.hpp"
#include "Epoller.hpp"
#include "Util.hpp"

using namespace std;

namespace Reactor_ns
{
    class Connection;
    class TcpServer;

    static const uint16_t gport = 8080;
    static const int gnum = 64;
    using func_t = function<void(Connection *)>;

    class Connection
    {
    public:
        Connection(int sock, TcpServer *tsp)
            : _sock(sock), _tsp(tsp)
        {
        }
        void Register(func_t recver, func_t sender, func_t excepter)
        {
            _recver = recver;
            _sender = sender;
            _excepter = excepter;
        }
        void Close()
        {
            close(_sock);
        }
        ~Connection()
        {
        }

    public:
        int _sock;
        string _inbuffer;  // 输入缓冲区
        string _outbuffer; // 输出缓冲区

        func_t _recver;   // 从sock里读
        func_t _sender;   // 向sock里写
        func_t _excepter; // 处理sock IO的时候上面的异常事件

        TcpServer *_tsp; // 回执指针
    };

    class TcpServer
    {
    public:
        TcpServer(func_t func, uint16_t port = gport, int num = gnum)
            : _service(func), _port(port), _revs(nullptr), _num(num)
        {
        }
        void InitServer()
        {
            // 1. 创建socket
            _sock.Socket();
            _sock.Bind(_port);
            _sock.Listen();
            // 2. 构建epoll
            _epoller.Create();
            // 3. 添加事件到epoller中，将对应的fd设置为非阻塞
            // listensock 也是socket，也要看作是一个connection
            AddConnection(_sock.getFd(), EPOLLIN | EPOLLET,
                          std::bind(&TcpServer::Accepter, this, std::placeholders::_1), nullptr, nullptr);

            _revs = new struct epoll_event[_num];
        }
        // 给对应的sock ，增加读、写事件
        void EnableReadWrite(Connection *conn, bool readr, bool writer)
        {
            uint32_t events = (readr ? EPOLLIN : 0) | (writer ? EPOLLOUT : 0) | EPOLLET;

            _epoller.Control(conn->_sock, events, EPOLL_CTL_MOD);
        }
        // 事件派发器
        void Dispatcher()
        {
            int timeout = -1;
            while (true)
            {
                Loop(timeout);
                // logMessage(DEBUG, "timeout...");
            }
        }
        ~TcpServer()
        {

            if (_revs)
                delete[] _revs;
        }

    private:
        // 读事件处理
        void Recver(Connection *conn)
        {
            char buffer[1024];
            while (true)
            {
                ssize_t s = recv(conn->_sock, buffer, sizeof(buffer) - 1, 0);
                if (s > 0)
                {
                    buffer[s] = 0;
                    conn->_inbuffer += buffer; // 将读取到的数据入队列
                }
                else if (s == 0)
                {
                    if (conn->_excepter)
                    {
                        conn->_excepter(conn);
                        conn = nullptr;
                        return;
                    }
                }
                else
                {
                    if (errno == EAGAIN || errno == EWOULDBLOCK)
                        break;
                    else if (errno == EINTR)
                        continue;
                    else
                    {
                        if (conn->_excepter)
                        {
                            conn->_excepter(conn);
                            conn = nullptr;
                            return;
                        }
                    }
                }
            }
            _service(conn);
        }
        // 写事件处理
        void Sender(Connection *conn)
        {
            while (true)
            {
                ssize_t s = send(conn->_sock, conn->_outbuffer.c_str(), conn->_outbuffer.size(), 0);
                if (s > 0)
                {
                    if (conn->_outbuffer.empty())
                        break;
                    else
                        conn->_outbuffer.erase(0, s);
                }
                else
                {
                    if (errno == EAGAIN || errno == EWOULDBLOCK)
                        break;
                    else if (errno == EINTR)
                        continue;
                    else
                    {
                        if (conn->_excepter)
                        {
                            conn->_excepter(conn);
                            conn = nullptr;
                            return;
                        }
                    }
                }
            }
            if (!conn->_outbuffer.empty())
                EnableReadWrite(conn, true, true);
            else
                EnableReadWrite(conn, true, false);
        }
        // 异常事件处理
        void Excepter(Connection *conn)
        {
            if (conn != nullptr)
            {
                _epoller.Control(conn->_sock, 0, EPOLL_CTL_DEL);
                conn->Close();

                _connections.erase(conn->_sock);

                logMessage(DEBUG, "%d  except quit", conn->_sock);
                delete conn;
            }
        }
        // 连接事件处理
        void Accepter(Connection *conn)
        {
            for (;;)
            {
                string clientip;
                uint16_t clinetport = 0;
                int err = 0;
                int sock = _sock.Accept(&clientip, &clinetport, &err);

                if (sock > 0)
                {
                    AddConnection(sock, EPOLLIN | EPOLLET,
                                  bind(&TcpServer::Recver, this, placeholders::_1),
                                  bind(&TcpServer::Sender, this, placeholders::_1),
                                  bind(&TcpServer::Excepter, this, placeholders::_1));
                    logMessage(DEBUG, "get a new link, info:[%s,%d]", clientip.c_str(), clinetport);
                }
                else
                {
                    if (err == EAGAIN || err == EWOULDBLOCK)
                        break;
                    else if (err == EINTR)
                        continue;
                    else
                        break;
                }
            }
        }
        void AddConnection(int sock, uint32_t events, func_t recver, func_t sender, func_t excepter)
        {
            // 1. 首先要为该sock创建connection，并初始化，且添加到connections里
            if (events & EPOLLET)
                Util::SetNonBlock(sock);
            Connection *conn = new Connection(sock, this);
            // 给对应的sock设置对应的回调方法
            conn->Register(recver, sender, excepter);

            // 2. 其次将sock与它要关心的事件 "写透式" 注册到epoll中，让epoll帮我们关心
            bool r = _epoller.AddEvent(sock, events);
            assert(r);
            (void)r;
            // 3. 将kv添加到connections
            _connections.insert(pair<int, Connection *>(sock, conn));
        }
        // 判断sock是否被管理起来
        bool IsconnectionsExists(int sock)
        {
            auto it = _connections.find(sock);
            return it != _connections.end();
        }
        // 处理就绪事件
        void Loop(int timeout)
        {
            int n = _epoller.Wait(_revs, _num, timeout);
            for (int i = 0; i < n; ++i)
            {
                int sock = _revs[i].data.fd;
                uint32_t events = _revs[i].events;

                // 将所有的异常问题，全部转化成读写问题，让读写去处理这个异常
                if (events & EPOLLERR)
                    events |= (EPOLLIN | EPOLLOUT);
                if (events & EPOLLHUP)
                    events |= (EPOLLIN | EPOLLOUT);

                //  就绪，sock存在，并且回调方法不能为空
                if ((events & EPOLLIN) && IsconnectionsExists(sock) && _connections[sock]->_recver != nullptr)
                    _connections[sock]->_recver(_connections[sock]);
                if ((events & EPOLLOUT) && IsconnectionsExists(sock) && _connections[sock]->_sender != nullptr)
                    _connections[sock]->_sender(_connections[sock]);
            }
        }

    private:
        uint16_t _port;
        Sock _sock;
        Epoller _epoller;
        unordered_map<int, Connection *> _connections;
        struct epoll_event *_revs;
        int _num;
        func_t _service;
    };
}